﻿// Inner Fire 游戏引擎库
// 场景分装 - 基本几何体
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-27

#include <dx_graphics.h>

namespace ifire {
void DxGraphics::Altar_Update() {
  Altar_UpdateObjectCBs();
  Altar_UpdateMaterialBuffer();
  UpdateMainPassCB();
}

void DxGraphics::Altar_InitPipeline() {
  Altar_LoadTextures();
  Altar_BuildRootSignature();
  Altar_BuildDescriptorHeaps();
  Altar_BuildShadersAndInputLayout();
  Altar_BuildShapeGeometry();
  Altar_BuildMaterials();
  Altar_BuildRenderItems();
  BuildFrameResources_V2();
  Altar_BuildPSOs();
}

void DxGraphics::Altar_Draw() {
  auto cmd = command_list_.Get();
  auto cmdListAlloc = current_frame_resource_->CmdListAlloc;

  // Reuse the memory associated with command recording.
  // We can only reset when the associated command lists have finished execution
  // on the GPU.
  ThrowIfFailed(cmdListAlloc->Reset());

  // A command list can be reset after it has been added to the command queue
  // via ExecuteCommandList. Reusing the command list reuses memory.
  ThrowIfFailed(cmd->Reset(cmdListAlloc.Get(), pipelines_["Opaque"].Get()));

  cmd->RSSetViewports(1, &screen_viewport_);
  cmd->RSSetScissorRects(1, &scissor_rect_);

  // Indicate a state transition on the resource usage.
  auto to_render = CD3DX12_RESOURCE_BARRIER::Transition(GetBackBuffer(),
      D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
  cmd->ResourceBarrier(1, &to_render);

  // Clear the back buffer and depth buffer.
  auto dsv = GetDepthStencilView();
  auto back_buffer = GetBackBufferView();
  cmd->ClearRenderTargetView(back_buffer, Colors::LightSteelBlue, 0, nullptr);
  cmd->ClearDepthStencilView(dsv,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

  // Specify the buffers we are going to render to.
  cmd->OMSetRenderTargets(1, &back_buffer, true, &dsv);

  ID3D12DescriptorHeap* descriptorHeaps[] = {srv_descriptor_heap_.Get()};
  cmd->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

  cmd->SetGraphicsRootSignature(root_signature_.Get());

  auto passCB = current_frame_resource_->PassCB->Resource();
  cmd->SetGraphicsRootConstantBufferView(1, passCB->GetGPUVirtualAddress());

  // Bind all the materials used in this scene.  For structured buffers, we can
  // bypass the heap and set as a root descriptor.
  auto matBuffer = current_frame_resource_->MaterialBuffer->Resource();
  cmd->SetGraphicsRootShaderResourceView(2, matBuffer->GetGPUVirtualAddress());

  // Bind all the textures used in this scene.  Observe
  // that we only have to specify the first descriptor in the table.
  // The root signature knows how many descriptors are expected in the table.
  cmd->SetGraphicsRootDescriptorTable(
      3, srv_descriptor_heap_->GetGPUDescriptorHandleForHeapStart());

  DrawRenderItems_V2(cmd, opaque_items_);

  // Indicate a state transition on the resource usage.
  auto to_present = CD3DX12_RESOURCE_BARRIER::Transition(GetBackBuffer(),
      D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
  cmd->ResourceBarrier(1, &to_present);

  // Done recording commands.
  ThrowIfFailed(cmd->Close());

  // Add the command list to the queue for execution.
  ID3D12CommandList* cmdsLists[] = {cmd};
  command_queue_->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

  // Swap the back and front buffers
  ThrowIfFailed(swap_chain_->Present(0, 0));
  current_back_buffer_ = (current_back_buffer_ + 1) % SWAP_CHAIN_BUFFER_COUNT;

  // Advance the fence value to mark commands up to this fence point.
  current_frame_resource_->Fence = ++current_fence_;

  // Add an instruction to the command queue to set a new fence point.
  // Because we are on the GPU timeline, the new fence point won't be
  // set until the GPU finishes processing all the commands prior to this
  // Signal().
  command_queue_->Signal(fence_.Get(), current_fence_);
}

void DxGraphics::Altar_LoadTextures() {
  auto bricksTex = std::make_unique<Texture>();
  bricksTex->Name = "bricksTex";
  bricksTex->Filename = "Textures/bricks.dds";
  ThrowIfFailed(LoadDDS(
      bricksTex->Filename.c_str(), bricksTex->Resource, bricksTex->UploadHeap));

  auto stoneTex = std::make_unique<Texture>();
  stoneTex->Name = "stoneTex";
  stoneTex->Filename = "Textures/stone.dds";
  ThrowIfFailed(LoadDDS(
      stoneTex->Filename.c_str(), stoneTex->Resource, stoneTex->UploadHeap));

  auto tileTex = std::make_unique<Texture>();
  tileTex->Name = "tileTex";
  tileTex->Filename = "Textures/tile.dds";
  ThrowIfFailed(LoadDDS(
      tileTex->Filename.c_str(), tileTex->Resource, tileTex->UploadHeap));

  auto crateTex = std::make_unique<Texture>();
  crateTex->Name = "crateTex";
  crateTex->Filename = "Textures/WoodCrate01.dds";
  ThrowIfFailed(LoadDDS(
      crateTex->Filename.c_str(), crateTex->Resource, crateTex->UploadHeap));

  textures_[bricksTex->Name] = std::move(bricksTex);
  textures_[stoneTex->Name] = std::move(stoneTex);
  textures_[tileTex->Name] = std::move(tileTex);
  textures_[crateTex->Name] = std::move(crateTex);
}

void DxGraphics::Altar_BuildRootSignature() {
  CD3DX12_DESCRIPTOR_RANGE texTable;
  texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 4, 0, 0);

  // Root parameter can be a table, root descriptor or root constants.
  CD3DX12_ROOT_PARAMETER slotRootParameter[4];

  // Perfomance TIP: Order from most frequent to least frequent.
  slotRootParameter[0].InitAsConstantBufferView(0);
  slotRootParameter[1].InitAsConstantBufferView(1);
  slotRootParameter[2].InitAsShaderResourceView(0, 1);
  // 这东西就是四个纹理存的地方
  slotRootParameter[3].InitAsDescriptorTable(
      1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL);

  auto staticSamplers = GetStaticSamplers();

  // A root signature is an array of root parameters.
  CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4, slotRootParameter,
      (UINT)staticSamplers.size(), staticSamplers.data(),
      D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // create a root signature with a single slot which points to a descriptor
  // range consisting of a single constant buffer
  ComPtr<ID3DBlob> serializedRootSig = nullptr;
  ComPtr<ID3DBlob> errorBlob = nullptr;
  HRESULT hr =
      D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
          serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

  if (errorBlob != nullptr) {
    ::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
  }
  ThrowIfFailed(hr);

  ThrowIfFailed(device_->CreateRootSignature(0,
      serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(),
      IID_PPV_ARGS(root_signature_.GetAddressOf())));
}

void DxGraphics::Altar_BuildDescriptorHeaps() {
  //
  // Create the SRV heap.
  //
  D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
  srvHeapDesc.NumDescriptors = 4;
  srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  ThrowIfFailed(device_->CreateDescriptorHeap(
      &srvHeapDesc, IID_PPV_ARGS(&srv_descriptor_heap_)));

  //
  // Fill out the heap with actual descriptors.
  //
  CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
      srv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart());

  auto bricksTex = textures_["bricksTex"]->Resource;
  auto stoneTex = textures_["stoneTex"]->Resource;
  auto tileTex = textures_["tileTex"]->Resource;
  auto crateTex = textures_["crateTex"]->Resource;

  D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  srvDesc.Format = bricksTex->GetDesc().Format;
  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srvDesc.Texture2D.MostDetailedMip = 0;
  srvDesc.Texture2D.MipLevels = bricksTex->GetDesc().MipLevels;
  srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
  device_->CreateShaderResourceView(bricksTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, cbv_srv_uav_descriptor_size_);

  srvDesc.Format = stoneTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = stoneTex->GetDesc().MipLevels;
  device_->CreateShaderResourceView(stoneTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, cbv_srv_uav_descriptor_size_);

  srvDesc.Format = tileTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = tileTex->GetDesc().MipLevels;
  device_->CreateShaderResourceView(tileTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, cbv_srv_uav_descriptor_size_);

  srvDesc.Format = crateTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = crateTex->GetDesc().MipLevels;
  device_->CreateShaderResourceView(crateTex.Get(), &srvDesc, hDescriptor);
}

void DxGraphics::Altar_BuildShadersAndInputLayout() {
  const D3D_SHADER_MACRO alphaTestDefines[] = {"ALPHA_TEST", "1", NULL, NULL};

  shaders_["standardVS"] = dxutils::compile_shader(
      "Shaders\\Default2.hlsl", nullptr, "VS", "vs_5_1");
  shaders_["opaquePS"] = dxutils::compile_shader(
      "Shaders\\Default2.hlsl", nullptr, "PS", "ps_5_1");

  input_layout_ = {
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
  };
}

void DxGraphics::Altar_BuildMaterials() {
  auto bricks0 = std::make_unique<Material>();
  bricks0->Name = "bricks0";
  bricks0->MatCBIndex = 0;
  bricks0->DiffuseSrvHeapIndex = 0;
  bricks0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  bricks0->FresnelR0 = XMFLOAT3(0.02f, 0.02f, 0.02f);
  bricks0->Roughness = 0.1f;

  auto stone0 = std::make_unique<Material>();
  stone0->Name = "stone0";
  stone0->MatCBIndex = 1;
  stone0->DiffuseSrvHeapIndex = 1;
  stone0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  stone0->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  stone0->Roughness = 0.3f;

  auto tile0 = std::make_unique<Material>();
  tile0->Name = "tile0";
  tile0->MatCBIndex = 2;
  tile0->DiffuseSrvHeapIndex = 2;
  tile0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  tile0->FresnelR0 = XMFLOAT3(0.02f, 0.02f, 0.02f);
  tile0->Roughness = 0.3f;

  auto crate0 = std::make_unique<Material>();
  crate0->Name = "crate0";
  crate0->MatCBIndex = 3;
  crate0->DiffuseSrvHeapIndex = 3;
  crate0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  crate0->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  crate0->Roughness = 0.2f;

  materials_["bricks0"] = std::move(bricks0);
  materials_["stone0"] = std::move(stone0);
  materials_["tile0"] = std::move(tile0);
  materials_["crate0"] = std::move(crate0);
}

void DxGraphics::Altar_BuildRenderItems() {
  auto boxRitem = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f) *
                                        XMMatrixTranslation(0.0f, 1.0f, 0.0f));
  XMStoreFloat4x4(&boxRitem->TexTransform, XMMatrixScaling(1.0f, 1.0f, 1.0f));
  boxRitem->ObjCBIndex = 0;
  boxRitem->Mat = materials_["crate0"].get();
  boxRitem->Geo = geometries_["shapeGeo"].get();
  boxRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
  boxRitem->StartIndexLocation =
      boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
  boxRitem->BaseVertexLocation =
      boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;
  items_.push_back(std::move(boxRitem));

  auto gridRitem = std::make_unique<RenderItem>();
  gridRitem->World = MathHelper::Identity4x4();
  XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(8.0f, 8.0f, 1.0f));
  gridRitem->ObjCBIndex = 1;
  gridRitem->Mat = materials_["tile0"].get();
  gridRitem->Geo = geometries_["shapeGeo"].get();
  gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
  gridRitem->StartIndexLocation =
      gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
  gridRitem->BaseVertexLocation =
      gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
  items_.push_back(std::move(gridRitem));

  XMMATRIX brickTexTransform = XMMatrixScaling(1.0f, 1.0f, 1.0f);
  UINT objCBIndex = 2;
  for (int i = 0; i < 5; ++i) {
    auto leftCylRitem = std::make_unique<RenderItem>();
    auto rightCylRitem = std::make_unique<RenderItem>();
    auto leftSphereRitem = std::make_unique<RenderItem>();
    auto rightSphereRitem = std::make_unique<RenderItem>();

    XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i * 5.0f);
    XMMATRIX rightCylWorld =
        XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i * 5.0f);

    XMMATRIX leftSphereWorld =
        XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i * 5.0f);
    XMMATRIX rightSphereWorld =
        XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i * 5.0f);

    XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
    XMStoreFloat4x4(&leftCylRitem->TexTransform, brickTexTransform);
    leftCylRitem->ObjCBIndex = objCBIndex++;
    leftCylRitem->Mat = materials_["bricks0"].get();
    leftCylRitem->Geo = geometries_["shapeGeo"].get();
    leftCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftCylRitem->IndexCount =
        leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    leftCylRitem->StartIndexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    leftCylRitem->BaseVertexLocation =
        leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
    XMStoreFloat4x4(&rightCylRitem->TexTransform, brickTexTransform);
    rightCylRitem->ObjCBIndex = objCBIndex++;
    rightCylRitem->Mat = materials_["bricks0"].get();
    rightCylRitem->Geo = geometries_["shapeGeo"].get();
    rightCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightCylRitem->IndexCount =
        rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
    rightCylRitem->StartIndexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
    rightCylRitem->BaseVertexLocation =
        rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

    XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
    leftSphereRitem->TexTransform = MathHelper::Identity4x4();
    leftSphereRitem->ObjCBIndex = objCBIndex++;
    leftSphereRitem->Mat = materials_["stone0"].get();
    leftSphereRitem->Geo = geometries_["shapeGeo"].get();
    leftSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    leftSphereRitem->IndexCount =
        leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    leftSphereRitem->StartIndexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    leftSphereRitem->BaseVertexLocation =
        leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
    rightSphereRitem->TexTransform = MathHelper::Identity4x4();
    rightSphereRitem->ObjCBIndex = objCBIndex++;
    rightSphereRitem->Mat = materials_["stone0"].get();
    rightSphereRitem->Geo = geometries_["shapeGeo"].get();
    rightSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    rightSphereRitem->IndexCount =
        rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
    rightSphereRitem->StartIndexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
    rightSphereRitem->BaseVertexLocation =
        rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

    items_.push_back(std::move(leftCylRitem));
    items_.push_back(std::move(rightCylRitem));
    items_.push_back(std::move(leftSphereRitem));
    items_.push_back(std::move(rightSphereRitem));
  }

  // All the render items are opaque.
  for (auto& e : items_) {
    opaque_items_.push_back(e.get());
  }
}
void DxGraphics::Altar_BuildSkullGeometry() {
  std::ifstream fin("Models/skull.txt");

  if (!fin) {
    MessageBox(0, "Models/skull.txt not found.", 0, 0);
    return;
  }

  UINT vcount = 0;
  UINT tcount = 0;
  std::string ignore;

  fin >> ignore >> vcount;
  fin >> ignore >> tcount;
  fin >> ignore >> ignore >> ignore >> ignore;

  XMFLOAT3 vMinf3(
      +MathHelper::Infinity, +MathHelper::Infinity, +MathHelper::Infinity);
  XMFLOAT3 vMaxf3(
      -MathHelper::Infinity, -MathHelper::Infinity, -MathHelper::Infinity);

  XMVECTOR vMin = XMLoadFloat3(&vMinf3);
  XMVECTOR vMax = XMLoadFloat3(&vMaxf3);

  std::vector<Vertex> vertices(vcount);
  for (UINT i = 0; i < vcount; ++i) {
    fin >> vertices[i].Pos.x >> vertices[i].Pos.y >> vertices[i].Pos.z;
    fin >> vertices[i].Normal.x >> vertices[i].Normal.y >> vertices[i].Normal.z;

    vertices[i].TexC = {0.0f, 0.0f};

    XMVECTOR P = XMLoadFloat3(&vertices[i].Pos);

    vMin = XMVectorMin(vMin, P);
    vMax = XMVectorMax(vMax, P);
  }

  BoundingBox bounds;
  XMStoreFloat3(&bounds.Center, 0.5f * (vMin + vMax));
  XMStoreFloat3(&bounds.Extents, 0.5f * (vMax - vMin));

  fin >> ignore;
  fin >> ignore;
  fin >> ignore;

  std::vector<std::int32_t> indices(3 * tcount);
  for (UINT i = 0; i < tcount; ++i) {
    fin >> indices[i * 3 + 0] >> indices[i * 3 + 1] >> indices[i * 3 + 2];
  }

  fin.close();

  //
  // Pack the indices of all the meshes into one index buffer.
  //

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);

  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::int32_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "skullGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R32_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SubmeshGeometry submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;
  submesh.Bounds = bounds;

  geo->DrawArgs["skull"] = submesh;

  mGeometries[geo->Name] = std::move(geo);
}
void DxGraphics::BuildFrameResources_V2() {
  FrameResourceDesc desc = {};
  desc.PassCount = 1;
  desc.ObjectCount = static_cast<UINT>(items_.size());
  desc.MaterialCount = static_cast<UINT>(materials_.size());

  for (int i = 0; i < FRAME_RESOURCE_COUNT; i++) {
    frame_resources_.push_back(
        std::make_unique<FrameResource>(device_.Get(), &desc));
  }
}

void DxGraphics::Altar_BuildPSOs() {
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;
  //
  // PSO for opaque objects.
  //
  ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
  opaquePsoDesc.InputLayout = {
      input_layout_.data(), (UINT)input_layout_.size()};
  opaquePsoDesc.pRootSignature = root_signature_.Get();
  opaquePsoDesc.VS = {
      reinterpret_cast<BYTE*>(shaders_["standardVS"]->GetBufferPointer()),
      shaders_["standardVS"]->GetBufferSize()};
  opaquePsoDesc.PS = {
      reinterpret_cast<BYTE*>(shaders_["opaquePS"]->GetBufferPointer()),
      shaders_["opaquePS"]->GetBufferSize()};
  opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaquePsoDesc.SampleMask = UINT_MAX;
  opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaquePsoDesc.NumRenderTargets = 1;
  opaquePsoDesc.RTVFormats[0] = back_buffer_format_;
  opaquePsoDesc.SampleDesc.Count = GetMsaaSampleCount();
  opaquePsoDesc.SampleDesc.Quality = GetMsaaSampleQuality();
  opaquePsoDesc.DSVFormat = depth_stencil_format_;
  // 用于不透明的对象
  HASSERT(device_->CreateGraphicsPipelineState(
      &opaquePsoDesc, IID_PPV_ARGS(&pipelines_["Opaque"])));
}

void DxGraphics::Altar_UpdateObjectCBs() {
  // 这里因为使用ObjectConstantsV2，所以也不同。
  auto currObjectCB = current_frame_resource_->ObjectCB_V2.get();
  for (auto& e : items_) {
    // Only update the cbuffer data if the constants have changed.
    // This needs to be tracked per frame resource.
    if (e->NumFramesDirty > 0) {
      XMMATRIX world = XMLoadFloat4x4(&e->World);
      XMMATRIX texTransform = XMLoadFloat4x4(&e->TexTransform);

      ObjectConstantsV2 objConstants;
      XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
      XMStoreFloat4x4(
          &objConstants.TexTransform, XMMatrixTranspose(texTransform));
      // 这里和其它的Update不同，是使用索引
      objConstants.MaterialIndex = e->Mat->MatCBIndex;

      currObjectCB->CopyData(e->ObjCBIndex, objConstants);

      // Next FrameResource need to be updated too.
      e->NumFramesDirty--;
    }
  }
}

void DxGraphics::Altar_UpdateMaterialBuffer() {
  auto currMaterialBuffer = current_frame_resource_->MaterialBuffer.get();
  for (auto& e : materials_) {
    // Only update the cbuffer data if the constants have changed.  If the
    // cbuffer data changes, it needs to be updated for each FrameResource.
    Material* mat = e.second.get();
    if (mat->NumFramesDirty > 0) {
      XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);

      // 这里使用MaterialData
      MaterialData matData;
      matData.DiffuseAlbedo = mat->DiffuseAlbedo;
      matData.FresnelR0 = mat->FresnelR0;
      matData.Roughness = mat->Roughness;
      XMStoreFloat4x4(&matData.MatTransform, XMMatrixTranspose(matTransform));
      matData.DiffuseMapIndex = mat->DiffuseSrvHeapIndex;

      currMaterialBuffer->CopyData(mat->MatCBIndex, matData);

      // Next FrameResource need to be updated too.
      mat->NumFramesDirty--;
    }
  }
}

void DxGraphics::DrawRenderItems_V2(ID3D12GraphicsCommandList* cmdList,
    const std::vector<RenderItem*>& ritems) {
  UINT objCBByteSize =
      dxutils::calc_constant_buffer_size(sizeof(ObjectConstantsV2));

  auto objectCB = current_frame_resource_->ObjectCB_V2->Resource();

  // For each render item...
  for (size_t i = 0; i < ritems.size(); ++i) {
    auto ri = ritems[i];

    auto vertex_view = ri->Geo->VertexBufferView();
    auto index_view = ri->Geo->IndexBufferView();
    cmdList->IASetVertexBuffers(0, 1, &vertex_view);
    cmdList->IASetIndexBuffer(&index_view);
    cmdList->IASetPrimitiveTopology(ri->PrimitiveType);

    // 这里就不用设置纹理做为SLOT
    D3D12_GPU_VIRTUAL_ADDRESS objCBAddress =
        objectCB->GetGPUVirtualAddress() + ri->ObjCBIndex * objCBByteSize;
    cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);

    cmdList->DrawIndexedInstanced(
        ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
  }
}

void DxGraphics::Altar_BuildShapeGeometry() {
  GeometryGenerator geoGen;
  GeometryGenerator::MeshData box = geoGen.CreateBox(1.0f, 1.0f, 1.0f, 3);
  GeometryGenerator::MeshData grid = geoGen.CreateGrid(20.0f, 30.0f, 60, 40);
  GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
  GeometryGenerator::MeshData cylinder =
      geoGen.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);

  //
  // We are concatenating all the geometry into one big vertex/index buffer.  So
  // define the regions in the buffer each submesh covers.
  //

  // Cache the vertex offsets to each object in the concatenated vertex buffer.
  UINT boxVertexOffset = 0;
  UINT gridVertexOffset = (UINT)box.Vertices.size();
  UINT sphereVertexOffset = gridVertexOffset + (UINT)grid.Vertices.size();
  UINT cylinderVertexOffset = sphereVertexOffset + (UINT)sphere.Vertices.size();

  // Cache the starting index for each object in the concatenated index buffer.
  UINT boxIndexOffset = 0;
  UINT gridIndexOffset = (UINT)box.Indices32.size();
  UINT sphereIndexOffset = gridIndexOffset + (UINT)grid.Indices32.size();
  UINT cylinderIndexOffset = sphereIndexOffset + (UINT)sphere.Indices32.size();

  SubmeshGeometry boxSubmesh;
  boxSubmesh.IndexCount = (UINT)box.Indices32.size();
  boxSubmesh.StartIndexLocation = boxIndexOffset;
  boxSubmesh.BaseVertexLocation = boxVertexOffset;

  SubmeshGeometry gridSubmesh;
  gridSubmesh.IndexCount = (UINT)grid.Indices32.size();
  gridSubmesh.StartIndexLocation = gridIndexOffset;
  gridSubmesh.BaseVertexLocation = gridVertexOffset;

  SubmeshGeometry sphereSubmesh;
  sphereSubmesh.IndexCount = (UINT)sphere.Indices32.size();
  sphereSubmesh.StartIndexLocation = sphereIndexOffset;
  sphereSubmesh.BaseVertexLocation = sphereVertexOffset;

  SubmeshGeometry cylinderSubmesh;
  cylinderSubmesh.IndexCount = (UINT)cylinder.Indices32.size();
  cylinderSubmesh.StartIndexLocation = cylinderIndexOffset;
  cylinderSubmesh.BaseVertexLocation = cylinderVertexOffset;

  //
  // Extract the vertex elements we are interested in and pack the
  // vertices of all the meshes into one vertex buffer.
  //

  auto totalVertexCount = box.Vertices.size() + grid.Vertices.size() +
                          sphere.Vertices.size() + cylinder.Vertices.size();

  std::vector<Vertex> vertices(totalVertexCount);

  UINT k = 0;
  for (size_t i = 0; i < box.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = box.Vertices[i].Position;
    vertices[k].Normal = box.Vertices[i].Normal;
    vertices[k].TexC = box.Vertices[i].TexC;
  }

  for (size_t i = 0; i < grid.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = grid.Vertices[i].Position;
    vertices[k].Normal = grid.Vertices[i].Normal;
    vertices[k].TexC = grid.Vertices[i].TexC;
  }

  for (size_t i = 0; i < sphere.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = sphere.Vertices[i].Position;
    vertices[k].Normal = sphere.Vertices[i].Normal;
    vertices[k].TexC = sphere.Vertices[i].TexC;
  }

  for (size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = cylinder.Vertices[i].Position;
    vertices[k].Normal = cylinder.Vertices[i].Normal;
    vertices[k].TexC = cylinder.Vertices[i].TexC;
  }

  std::vector<std::uint16_t> indices;
  indices.insert(indices.end(), std::begin(box.GetIndices16()),
      std::end(box.GetIndices16()));
  indices.insert(indices.end(), std::begin(grid.GetIndices16()),
      std::end(grid.GetIndices16()));
  indices.insert(indices.end(), std::begin(sphere.GetIndices16()),
      std::end(sphere.GetIndices16()));
  indices.insert(indices.end(), std::begin(cylinder.GetIndices16()),
      std::end(cylinder.GetIndices16()));

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "shapeGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  geo->DrawArgs["box"] = boxSubmesh;
  geo->DrawArgs["grid"] = gridSubmesh;
  geo->DrawArgs["sphere"] = sphereSubmesh;
  geo->DrawArgs["cylinder"] = cylinderSubmesh;

  geometries_[geo->Name] = std::move(geo);
}

} // namespace ifire